/*
 * Copyright (c) 2023-2024 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.util.lang;

import java.util.Collection;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 正则相关工具类
 *
 * @author zeng
 */
public class RegExpUtils {
  /** 分组 */
  public static final Pattern GROUP_VAR = Pattern.compile("\\$(\\d+)");

  /** 正则中需要被转义的关键字 */
  public static final Set<Character> RE_KEYS =
      Set.of('$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|');

  /**
   * 正则替换指定值<br>
   * 通过正则查找到字符串，然后把匹配到的字符串加入到replacementTemplate中，$1表示分组1的字符串
   *
   * @param content 文本
   * @param pattern {@link Pattern}
   * @param replacementTemplate 替换的文本模板，可以使用$1类似的变量提取正则匹配出的内容
   * @return 处理后的文本
   * @since 3.0.4
   */
  public static String replaceAll(
      CharSequence content, Pattern pattern, String replacementTemplate) {
    if (StringUtils.isEmpty(content)) {
      return StringUtils.str(content);
    }

    final Matcher matcher = pattern.matcher(content);
    boolean result = matcher.find();
    if (result) {
      final Set<String> varNums =
          findAll(
              GROUP_VAR,
              replacementTemplate,
              1,
              new TreeSet<>(LengthComparator.INSTANCE.reversed()));
      final StringBuffer sb = new StringBuffer();
      do {
        String replacement = replacementTemplate;
        for (final String var : varNums) {
          final int group = Integer.parseInt(var);
          replacement = replacement.replace("$" + var, matcher.group(group));
        }
        matcher.appendReplacement(sb, escape(replacement));
        result = matcher.find();
      } while (result);
      matcher.appendTail(sb);
      return sb.toString();
    }
    return StringUtils.str(content);
  }

  /**
   * 转义字符串，将正则的关键字转义
   *
   * @param content 文本
   * @return 转义后的文本
   */
  public static String escape(CharSequence content) {
    if (StringUtils.isBlank(content)) {
      return StringUtils.str(content);
    }

    final StringBuilder builder = new StringBuilder();
    int len = content.length();
    char current;
    for (int i = 0; i < len; i++) {
      current = content.charAt(i);
      if (RE_KEYS.contains(current)) {
        builder.append('\\');
      }
      builder.append(current);
    }
    return builder.toString();
  }

  /**
   * 取得内容中匹配的所有结果
   *
   * @param <T> 集合类型
   * @param pattern 编译后的正则模式
   * @param content 被查找的内容
   * @param group 正则的分组
   * @param collection 返回的集合类型
   * @return 结果集
   */
  public static <T extends Collection<String>> T findAll(
      Pattern pattern, CharSequence content, int group, T collection) {
    if (null == pattern || null == content) {
      return null;
    }
    Assert.notNull(collection, "Collection must be not null !");

    findAll(pattern, content, (matcher) -> collection.add(matcher.group(group)));
    return collection;
  }

  /**
   * 取得内容中匹配的所有结果，使用{@link Consumer}完成匹配结果处理
   *
   * @param pattern 编译后的正则模式
   * @param content 被查找的内容
   * @param consumer 匹配结果处理函数
   * @since 5.7.15
   */
  public static void findAll(Pattern pattern, CharSequence content, Consumer<Matcher> consumer) {
    if (null == pattern || null == content) {
      return;
    }

    final Matcher matcher = pattern.matcher(content);
    while (matcher.find()) {
      consumer.accept(matcher);
    }
  }

  /**
   * 字符串长度比较器，短在前
   *
   * @author looly
   * @since 5.8.9
   */
  public static class LengthComparator implements Comparator<CharSequence> {
    /** 单例的字符串长度比较器，短在前 */
    public static final LengthComparator INSTANCE = new LengthComparator();

    @Override
    public int compare(CharSequence o1, CharSequence o2) {
      int result = Integer.compare(o1.length(), o2.length());
      if (0 == result) {
        return o1.toString().compareTo(o2.toString());
      }
      return result;
    }
  }
}
